package org.infodavid.common.naming;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.infodavid.common.net.NetUtil;

/**
 * The Class NamingUtil.
 */
public final class NamingUtil {

  /** Constant CONNECTION_FACTORY_JNDI_NAME. */
  public static final String CONNECTION_FACTORY_JNDI_NAME = "/ConnectionFactory";

  /** The default address (without protocol). */
  public static final String DEFAULT_ADDRESS = "127.0.0.1:1099";

  /** Default value of DEFAULT_FACTORY_INITIAL. */
  protected static final String DEFAULT_FACTORY_INITIAL = "org.jnp.interfaces.NamingContextFactory";

  /** Default value of DEFAULT_PKGS. */
  protected static final String DEFAULT_PKGS = "org.jboss.naming:org.jnp.interfaces";

  /** The default port. */
  public static final int DEFAULT_PORT = 1099;

  /** The default timeout in ms for RMI connections. */
  public static final int DEFAULT_RMI_TCP_TIMEOUT_IN_MS = 15000;

  /** The Constant HOSTNAME. */
  public static final String HOSTNAME;

  /** The Constant LOG. */
  private static final Log LOGGER = LogFactory.getLog(NamingUtil.class);

  /** The Constant isDebugEnabled. */
  private static final boolean isDebugEnabled = LOGGER.isDebugEnabled();

  /** Initial naming factory (specific). */
  protected static final String NAMING_FACTORY_INITIAL_KEY = "java.naming.factory.initial";

  /** Naming factory. */
  protected static final String NAMING_FACTORY_URL_PKGS_KEY = "java.naming.factory.url.pkgs";

  /** Naming provider URL (where to lookup). */
  protected static final String NAMING_PROVIDER_URL_KEY = "java.naming.provider.url";

  /**
   * The property name used to set and get the timeout for RMI connections.
   */
  protected static final String RMI_CONNECT_TIMEOUT_IN_MS_PROPERTY =
      "sun.rmi.transport.connectionTimeout";

  /**
   * The property name used to set and get the timeout for RMI proxy connections.
   */
  protected static final String RMI_HANDSHAKE_TIMEOUT_IN_MS_PROPERTY =
      "sun.rmi.transport.tcp.handshakeTimeout ";

  /**
   * The property name used to set and get the timeout for RMI proxy connections.
   */
  protected static final String RMI_PROXY_CONNECT_TIMEOUT_IN_MS_PROPERTY =
      "sun.rmi.transport.proxy.connectTimeout";

  /**
   * The property name used to set and get the timeout for RMI connections.
   */
  public static final String RMI_TCP_TIMEOUT_IN_MS_SYSTEM_PROPERTY =
      "sun.rmi.transport.tcp.responseTimeout";

  /** Constant USER_TRANSACTION_JNDI_NAME. */
  public static final String USER_TRANSACTION_JNDI_NAME = "UserTransaction";

  static {
    HOSTNAME = NetUtil.getInstance().getHostString();
  }

  /**
   * <p>
   * Build the context used to lookup factory and connection.
   * @param pAddress the address
   * @return A lookup context.
   * @throws NamingException If an error occurs.
   */
  public static Context getContext(final String... pAddress) throws NamingException {
    Context context;

    if (pAddress == null || pAddress.length == 0 || pAddress[0] == null) {
      if (isDebugEnabled) {
        LOGGER
            .debug("No provider address specified, using  the address described in jndi.properties");
      }

      context = new InitialContext();
    }
    else {
      final Properties props = new Properties();
      final String timeout = String.valueOf(getTimeoutInMs());

      props.put(NAMING_PROVIDER_URL_KEY, pAddress[0]);
      props.put(RMI_CONNECT_TIMEOUT_IN_MS_PROPERTY, timeout);
      props.put(RMI_PROXY_CONNECT_TIMEOUT_IN_MS_PROPERTY, timeout);
      props.put(RMI_TCP_TIMEOUT_IN_MS_SYSTEM_PROPERTY, timeout);
      props.put(RMI_HANDSHAKE_TIMEOUT_IN_MS_PROPERTY, timeout);

      if (isDebugEnabled) {
        LOGGER.debug(String.format("Using remote lookup url: %s : %s", NAMING_PROVIDER_URL_KEY,
            pAddress[0]));
      }

      context = new InitialContext(props);
    }

    return context;
  }

  /**
   * Gets the RMI timeout in ms.
   * @return the timeout
   */
  public static int getTimeoutInMs() {
    final String value = System.getProperty(RMI_TCP_TIMEOUT_IN_MS_SYSTEM_PROPERTY);
    int result = DEFAULT_RMI_TCP_TIMEOUT_IN_MS;

    if (StringUtils.isNotEmpty(value)) {
      try {
        result = Double.valueOf(value).intValue();
      }
      catch (final Exception e) {
        result = -1;
      }
    }

    if (result < 0) {
      result = DEFAULT_RMI_TCP_TIMEOUT_IN_MS;
    }

    return result;
  }

  /**
   * Validate the given address.
   * @param pAddress the address to check
   * @return the validated address
   */
  public static String validateAddress(final String pAddress) {
    String result = pAddress;

    if (!result.startsWith("jnp://")) {
      result = "jnp://" + result;
    }

    if (!result.matches("(.)*:([0-9]*)")) {
      result += ":" + DEFAULT_PORT;
    }

    return result;
  }

  /**
   * No instance for this utilities class.
   */
  private NamingUtil() {
    super();
  }
}
